﻿using System;
using System.Configuration;
using System.Linq;
using System.Linq.Expressions;
using System.Net.Http;
using System.Threading;
using Common.Logging;
using In.Api.Dto.HisWs;
using Newtonsoft.Json;
using Out.Service.Helpers;
using Out.Service.HttpClients;
using OutDbContext;
using Rmq.Center;

namespace Out.Service.Services
{
    public class BizInpatientPrepayOrder : IDisposable
    {
        //支付状态：0未支付 1支付中 2支付成功 3支付失败 4退款中 5退款成功 
        //6退款失败 7缴费中 8缴费成功 9缴费失败 10窗口退款成功
        public enum OrderStatus
        {
            未支付 = 0,
            支付中 = 1,
            支付成功 = 2,
            支付失败 = 3,
            退款中 = 4,
            退款成功 = 5,
            退款失败 = 6,
            缴费中 = 7,
            缴费成功 = 8,
            缴费失败 = 9,
            窗口退款成功=10
        }


        /// <summary>
        /// 支付状态:0-未付款,1-已付款,2-退款中,3-已退款,4-退款失败
        /// </summary>
        public enum PayState
        {
            未付款 = 0,
            已付款 = 1,
            退款中 = 2,
            已退款 = 3,
            退款失败 = 4
        }


        private static readonly ILog Logger = LogManager.GetLogger(typeof(BizInpatientPrepayOrder));

        private static readonly object OrderNoLocker = new object();

        private readonly GdsfybjyWeiXinEntities _ctx;
        private int _orderId;
        private readonly string _openId;

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="openId">用户openid</param>
        public BizInpatientPrepayOrder(string openId = "")
        {
            _ctx = new GdsfybjyWeiXinEntities();
            _openId = openId;
        }

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="orderId">订单Id</param>
        /// <param name="openId">用户openid</param>
        public BizInpatientPrepayOrder(int orderId, string openId = "")
        {
            _ctx = new GdsfybjyWeiXinEntities();
            _orderId = orderId;
            _openId = openId;
        }

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="orderNo">订单号</param>
        /// <param name="openId">用户openid</param>
        public BizInpatientPrepayOrder(string orderNo, string openId = "")
        {
            _ctx = new GdsfybjyWeiXinEntities();
            var order = _ctx.InpatientPrepayOrders.FirstOrDefault(x => x.OrderNo == orderNo);
            if (order != null) _orderId = order.Id;
            _openId = openId;
        }

        /// <summary>
        /// 获取订单数据对象
        /// </summary>
        /// <returns></returns>
        public InpatientPrepayOrder Get()
        {
            var order = _ctx.InpatientPrepayOrders.FirstOrDefault(x => x.Id == _orderId);
            return order;
        }



        /// <summary>
        /// 获取用户的所有订单数据
        /// </summary>
        /// <param name="startDt">查询开始日期</param>
        /// <returns></returns>
        public IQueryable<InpatientPrepayOrder> GetAll(DateTime startDt,DateTime endDt)
        {
            var orders = _ctx.InpatientPrepayOrders.IsOpenId(_openId).IsStatus(OrderStatus.缴费成功)
                .Where(x =>  x.CreateTime >= startDt && x.CreateTime < endDt).OrderByDescending(x => x.CreateTime);

            return orders;
        }

        /// <summary>
        /// 生成缴费订单单号
        /// </summary>
        /// <returns></returns>
        public string GenerateNewOrderNo()
        {
            var orderNo = "WX" + DateTime.Now.Date.ToString("yyyyMMdd");
            lock (OrderNoLocker)
            {
                orderNo += (DateTime.Now - DateTime.Now.Date).TotalMilliseconds.ToString("00000000");
                Thread.Sleep(1);
            }

            return orderNo;
        }

        /// <summary>
        /// 根据就诊卡号得到待缴费单据列表
        /// </summary>
        /// <param name="cardNo">就诊卡号</param>
        public ResResult<GetIcCardHolderStateResponse> InpatientPrepay(string cardNo)
        {
            //获取院内webservice
            var request = new GetIcCardHolderStateRequest();
            request.CardNo = cardNo;

            GetIcCardHolderStateResponse response;
            try
            {
                var httpClient = InHttpClient.GetClient();
                var responseMsg = httpClient.PostAsJsonAsync("HisWs/GetIcCardHolderState", request).Result;
                responseMsg.EnsureSuccessStatusCode();
                response = responseMsg.Content.ReadAsAsync<GetIcCardHolderStateResponse>().Result;
            }
            catch (Exception ex)
            {
                Logger.ErrorFormat("调用院内获取缴费单Api出错,信息：{0}", JsonConvert.SerializeObject(ex));

                return new ResResult<GetIcCardHolderStateResponse>(Result.未返回);
            }
            if (response.ResultCode == "0")
            {
                return new ResResult<GetIcCardHolderStateResponse>(Result.成功, response);
            }
            else
            {
                return new ResResult<GetIcCardHolderStateResponse>(Result.失败,response, "没有查询到您的在院信息");
            }

        }

        /// <summary>
        /// 生成预交金订单
        /// </summary>
        /// <param name="cardType">就诊卡卡类型</param>
        /// <param name="cardNo">就诊卡号</param>
        /// <param name="prepayMoney">预交金额</param>
        /// <returns></returns>
        public ResResult<string> CreateInpatientPrepayOrder(string cardType,  string cardNo, string prepayMoney)
        {
            try
            {
                var newOrderNo = GenerateNewOrderNo();
                var newPrepayOrder = new InpatientPrepayOrder()
                {
                    CardNo = cardNo,
                    CardType = cardType,
                    OpenId = _openId,
                    CreateTime = DateTime.Now,
                    PayState = "0",
                    OrderStatus = 0,
                    PrepayMoney = prepayMoney.ToDecimalOrDefault(),
                    InpatientId = "",
                    VisitId = "",
                    OrderNo = newOrderNo
                };

                _ctx.InpatientPrepayOrders.Add(newPrepayOrder);
                _ctx.SaveChanges();

                return new ResResult<string>(Result.成功, newOrderNo,"");
            }
            catch(Exception ex)
            {
                return new ResResult<string>(Result.失败, ex.Message);
            }
           
        }


        /// <summary>
        /// 执行缴费支付操作 (前提： 微信支付状态为 未支付（0））
        /// </summary>
        public ResResult<string> Paying(string clientIp)
        {
            var payState = PayState.未付款.ToInt32().ToString();
            var order = _ctx.InpatientPrepayOrders.FirstOrDefault( x => x.Id == _orderId && x.PayState == payState);
            if (order != null)
            {
                order.OrderStatus = OrderStatus.支付中.ToInt32();
                _ctx.SaveChanges();
                // 费用为0时直接设置为支付成功
                if (order.PrepayMoney == 0)
                {
                    PaySucceed(DateTime.Now, "0");
                    return new ResResult<string>(Result.成功, "", "");
                }
            }
            else
            {
                return new ResResult<string>(Result.条件不足未执行);
            }

            if (order.OrderStatus == OrderStatus.支付中.ToInt32())
            {
                // RMQ 16分钟后执行支付失败操作
                var payFailedProducer = new OrderPayFailedProducer();
                payFailedProducer.Publish(new OrderPayFailedMsg
                {
                    OrderType = Rmq.Center.OrderType.住院预缴金订单,
                    OrderId = order.Id,
                    OrderStatus = new[] {OrderStatus.支付中.ToInt32()}
                }, 16*60*1000);

                //组织 微信 js 支付方法 getBrandWCPayRequest 的参数
                var payStartTime = System.DateTime.Now.ToString("yyyyMMddHHmmss");
                var payExpireTime = System.DateTime.Now.AddMinutes(15).ToString("yyyyMMddHHmmss");

                var payFee = (order.PrepayMoney * 100).ToInt32().ToString();
                var superOpenIds = ConfigurationManager.AppSettings["OpenIdsForPayMin"]
                    .Split(" , ", StringSplitOptions.RemoveEmptyEntries);
                if (superOpenIds.Contains(order.OpenId))
                {
                    payFee = "1";
                }

                var payRequestParam = TenpayV2Helper.GenerateWxPayParameter("预交金缴费", "IP" + order.OrderNo,
                    payFee, clientIp, payStartTime, payExpireTime);

                return new ResResult<string>(Result.成功, payRequestParam, "");
            }
            else if (order.OrderStatus == OrderStatus.支付成功.ToInt32())
            {
                //执行HIS缴费,改为消息中心处理
                //DoPrescription();;
                var repostProducer = new OrderPickupingProducer();
                repostProducer.Publish(new OrderPickupingMsg
                {
                    OrderType = Rmq.Center.OrderType.门诊缴费订单,
                    OrderId = _orderId,
                    OrderStatus = new[] { OrderStatus.支付成功.ToInt32() }
                }, 1000);
                return new ResResult<string>(Result.失败, "订单已缴费成功", "");
            }
            else
            {
                return new ResResult<string>(Result.条件不足未执行);
            }
        }

        /// <summary>
        /// 取消预交金支付
        /// </summary>
        public ResResult<string> CancelPay()
        {
            try
            {
                var order = _ctx.InpatientPrepayOrders.IsStatus(OrderStatus.未支付, OrderStatus.支付中).FirstOrDefault(
                    x => x.Id == _orderId);
                if (order != null)
                {
                    order.OrderStatus = OrderStatus.支付失败.ToInt32();

                    _ctx.SaveChanges();

                }
            }
            catch(Exception ex)
            {
                Logger.ErrorFormat("取消预交金失败,信息：{0}", JsonConvert.SerializeObject(ex));
                return new ResResult<string>(Result.失败, ex.Message);
            }
            return new ResResult<string>(Result.成功);
        }

        /// <summary>
        /// 执行缴费操作 （前提：订单状态为 支付成功（2））
        /// </summary>
        public ResResult<string> DoPrescription(bool autoFeedback = true)
        {
            var order = _ctx.InpatientPrepayOrders.IsStatus(OrderStatus.支付成功).FirstOrDefault(x => x.Id == _orderId );
            if (order != null)
            {
                order.OrderStatus = OrderStatus.缴费中.ToInt32();
                _ctx.SaveChanges();
            }
            else
            {
                return new ResResult<string>(Result.条件不足未执行);
            }

            #region 预交金缴费
            //执行缴费操作
            var request = new IcCardTranToPrepayRequest();
            request.CardNo = order.CardNo;
            request.TransNo = order.WxTransactionId;
            request.TransTime = order.PayTime.GetValueOrDefault().ToString("yyyy-MM-dd HH:mm:ss");
            request.Zje = order.PrepayMoney.ToString();

            IcCardTranToPrepayResponse response;
            try
            {
                var httpClient = InHttpClient.GetClient();
                var responseMsg = httpClient.PostAsJsonAsync("HisWs/IcCardTranToPrepay", request).Result;
                responseMsg.EnsureSuccessStatusCode();
                response = responseMsg.Content.ReadAsAsync<IcCardTranToPrepayResponse>().Result;
            }
            catch (Exception ex)
            {
                Logger.ErrorFormat("调用院内预交金缴费Api出错,信息：{0}", JsonConvert.SerializeObject(ex));
                DoPrescriptionFail("调用院内充值服务失败退款");
                return new ResResult<string>(Result.未返回);
            }

            //执行缴费失败
                if (response.ResultCode != "0")
                {
                    if (autoFeedback)
                    {
                        DoPrescriptionFail("调用院内充值服务失败退款");
                    }
                    return new ResResult<string>(Result.失败, response.ResultDesc);
                }
                //执行缴费成功
                else
                {
                    if (autoFeedback)
                    {
                        DoPrescriptionSuccess();
                    }
                    return new ResResult<string>(Result.成功);
                }
                #endregion
        }

        /// <summary>
        /// 执行缴费成功处理
        /// </summary>
        public void DoPrescriptionSuccess()
        {
            var order = _ctx.InpatientPrepayOrders.IsStatus(OrderStatus.缴费中).FirstOrDefault(x => x.Id == _orderId );
            if (order != null )
            {
                order.OrderStatus = OrderStatus.缴费成功.ToInt32();
                _ctx.SaveChanges();

                // TODO RMQ 发送用户挂号成功通知
                // TODO RMQ 发送发货通知

            }
        }

        /// <summary>
        /// 执行缴费失败处理
        /// </summary>
        public void DoPrescriptionFail(string remarik)
        {
            var order =
                _ctx.InpatientPrepayOrders.IsStatus(OrderStatus.缴费中).FirstOrDefault(x => x.Id == _orderId);
            if (order != null)
            {
                order.OrderStatus = OrderStatus.缴费失败.ToInt32();
                order.Remark = remarik;
                _ctx.SaveChanges();

                if (order.PrepayMoney > 0)
                {
                    // RMQ 执行退款操作
                    var refundingProducer = new OrderRefundingProducer();
                    refundingProducer.Publish(new OrderRefundingMsg
                    {
                        OrderType = Rmq.Center.OrderType.住院预缴金订单,
                        OrderId = order.Id,
                        Reason = 1,
                        OrderStatus = new[] { OrderStatus.缴费失败.ToInt32() }
                    }, 1000);
                }

            }

        }

        ///  <summary>
        ///  缴费单支付成功的处理
        ///  </summary>
        /// <param name="payDt">支付成功时间</param>
        /// <param name="transactionId">支付交易号</param>
        public void PaySucceed(DateTime payDt, string transactionId)
        {
            var order = _ctx.InpatientPrepayOrders.IsStatus(OrderStatus.支付中).FirstOrDefault(x => x.Id == _orderId );
            if (order != null)
            {
                order.PayState = PayState.已付款.ToInt32().ToString();
                order.PayTime = payDt;
                order.WxTransactionId = transactionId;
                order.OrderStatus = OrderStatus.支付成功.ToInt32();
                _ctx.SaveChanges();

                //执行HIS缴费,改为消息中心处理
                //DoPrescription();;
                var repostProducer = new OrderPickupingProducer();
                repostProducer.Publish(new OrderPickupingMsg
                {
                    OrderType = Rmq.Center.OrderType.住院预缴金订单,
                    OrderId = _orderId,
                    OrderStatus = new[] { OrderStatus.支付成功.ToInt32() }
                }, 1000);
            }

        }

        /// <summary>
        /// 16分钟过后轮训订单状态，执行取消支付
        /// </summary>
        /// <returns></returns>
        public void PayFailed()
        {
            var order = _ctx.InpatientPrepayOrders.IsStatus(OrderStatus.支付中).FirstOrDefault(x => x.Id == _orderId );
            if (order != null)
            {
                order.OrderStatus = OrderStatus.支付失败.ToInt32();

                _ctx.SaveChanges();
            }
        }


        /// <summary>
        /// 执行退费操作
        /// </summary>
        /// <param name="autoFeedback">是否自动做成功/失败数据处理</param>
        /// <returns></returns>
        public ResResult<RefundResult> Refunding(bool autoFeedback = true)
        {
            var order = _ctx.InpatientPrepayOrders.IsStatus(OrderStatus.缴费失败).FirstOrDefault(x => x.Id == _orderId);
            if (order != null)
            {
                order.OrderStatus = OrderStatus.退款中.ToInt32();
                order.PayState = PayState.退款中.ToInt32().ToString();

                _ctx.SaveChanges();
            }
            else
            {
                return new ResResult<RefundResult>(Result.条件不足未执行);
            }

            RefundResult refundResult;
            try
            {
                //var paylog = _ctx.OrderNotifyLogs.OrderByDescending(x => x.Id).FirstOrDefault(x => x.OutTradeNo == "BL" + order.OrderNo);
                var refundAmount = (order.PrepayMoney * 100).ToInt32();
                //if (paylog != null && paylog.TotalFee.ToInt32OrDefault() != refundAmount)
                //{
                //    refundAmount = paylog.TotalFee.ToInt32OrDefault();
                //}
                refundResult = TenpayV2Helper.DoRefund(order.OpenId, "IP" + order.OrderNo, order.WxTransactionId,
                 "IP" + order.OrderNo, refundAmount.ToInt32(), refundAmount.ToInt32());
            }
            catch (Exception ex)
            {
                Logger.ErrorFormat("调用第三方支付退款Api出错,信息：{0}", JsonConvert.SerializeObject(ex));
                return new ResResult<RefundResult>(Result.未返回);
            }
            //退款成功 4,10
            //退款处理中 8 9 11
            if (refundResult.RefundStatus == 4 || refundResult.RefundStatus == 10 ||
                refundResult.RefundStatus == 8 || refundResult.RefundStatus == 9 || refundResult.RefundStatus == 11)
            {
                if (autoFeedback)
                {
                    RefundSucceed(refundResult);
                }

                return new ResResult<RefundResult>(Result.成功, refundResult);
            }
            //退款失败 3，5，6
            //转入代发 7
            if (refundResult.RefundStatus == 7 ||
                refundResult.RefundStatus == 3 || refundResult.RefundStatus == 5 || refundResult.RefundStatus == 6)
            {
                if (autoFeedback)
                {
                    RefundFailed();
                }
                return new ResResult<RefundResult>(Result.失败, refundResult, "");
            }

            return new ResResult<RefundResult>(Result.未返回);
        }


        /// <summary>
        /// 退费成功的处理
        /// </summary>
        /// <param name="resultResult"></param>
        public void RefundSucceed(RefundResult resultResult)
        {
            var order = _ctx.InpatientPrepayOrders.IsStatus(OrderStatus.退款中).FirstOrDefault(x => x.Id == _orderId );
            if (order != null)
            {
                order.OrderStatus = OrderStatus.退款成功.ToInt32();
                order.PayState = PayState.已退款.ToInt32().ToString();
                order.RefundTime = DateTime.Now;
                order.WxRefundId = resultResult.RefundId;
                _ctx.SaveChanges();


                //TODO RMQ 发送用户退费成功通知
                //TODO RMQ 发送发货通知，发货失败

            }
        }

        /// <summary>
        /// 退费失败的处理
        /// </summary>
        public void RefundFailed()
        {
            var order = _ctx.InpatientPrepayOrders.IsStatus(OrderStatus.退款中).FirstOrDefault(x => x.Id == _orderId);
            if (order != null)
            {
                order.OrderStatus = OrderStatus.退款失败.ToInt32();
                order.PayState = PayState.退款失败.ToInt32().ToString();
                _ctx.SaveChanges();

                //TODO RMQ 发送发货通知，发货失败
            }

        }





        /// <summary>
        /// 资源释放
        /// </summary>
        public void Dispose()
        {
            _ctx.Dispose();
        }
    }

    public static class InpatientPrepayOrderExtensions
    {
        public static IQueryable<InpatientPrepayOrder> IsOpenId(this IQueryable<InpatientPrepayOrder> source, string openId)
        {
            if (string.IsNullOrEmpty(openId))
            {
                return source;
            }

            return source.Where(x => x.OpenId == openId);
        }

        public static IQueryable<InpatientPrepayOrder> IsStatus(this IQueryable<InpatientPrepayOrder> source,
            params BizInpatientPrepayOrder.OrderStatus[] orderStatuses)
        {
            Expression<Func<InpatientPrepayOrder, bool>> expression = x => true;

            foreach (var orderStatus in orderStatuses)
            {
                var intOrderStatus = (int)orderStatus;
                Expression<Func<InpatientPrepayOrder, bool>> exp = x => x.OrderStatus == intOrderStatus;
                var body = Expression.OrElse(expression.Body, exp.Body);
                expression = Expression.Lambda<Func<InpatientPrepayOrder, bool>>(body, expression.Parameters[0]);
            }

            return source.Where(expression);
        }


    }


}
